[feat][node] Generalize request correlation - #124
Conversation
There was a problem hiding this comment.
Pull request overview
Generalizes node request correlation to support multiple response types.
Changes:
- Adds typed search and max-level waiters with RAII cleanup.
- Implements asynchronous max-level requests and response routing.
- Adds concurrency and timeout tests; updates private-file ignores.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
src/node/waiter.rs |
Defines waiter variants and cleanup guard. |
src/node/base_node.rs |
Adds max-level requests, routing, and tests. |
src/node/mod.rs |
Registers the waiter module. |
.gitignore |
Renames the private-material ignore path. |
Suppressed comments (1)
src/node/base_node.rs:305
- This similarly removes any waiter before checking its kind. A
RetMaxLevelOpwith a live search nonce silently evicts theSearchsender, so the blocking search fails instead of remaining pending. Preserve entries whose variant does not match this response type.
let waiter = self
.request_id_map
.lock()
.expect("mutex was poisoned by a previous panic")
.remove(&res.nonce);
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if let Err(e) = self.net.send_event( | ||
| introducer, | ||
| GetMaxLevelOp(MaxLevelReq { | ||
| nonce, | ||
| origin: self.core.id(), | ||
| }), |
There was a problem hiding this comment.
Correct that the arm is missing today, but that's intentional scope for this PR, not an oversight. get_max_level is still #[allow(dead_code)] with a TODO naming the follow-up wiring, its own doc comment calls it a latency optimization rather than a correctness requirement, and the introducer-side handler (computing the highest populated level plus the RetMaxLevelOp reply) is tracked as separate follow-up work sequenced after this one. This PR's scope is the request-correlation mechanism (the asking side), not the new Core operation the responder needs.
| @@ -209,10 +285,38 @@ impl EventProcessorCore for BaseNode { | |||
| .lock() | |||
| .expect("mutex was poisoned by a previous panic") | |||
| .remove(&res.nonce); | |||
There was a problem hiding this comment.
| #[allow(dead_code)] // TODO: remove once phase-0 bootstrap is wired into join orchestration. | ||
| pub(crate) async fn get_max_level( | ||
| &self, | ||
| introducer: Identifier, | ||
| timeout: Duration, | ||
| ) -> anyhow::Result<LookupTableLevel> { | ||
| let span = tracing::trace_span!("get_max_level", introducer = ?introducer); | ||
| let _enter = span.enter(); |
There was a problem hiding this comment.
Fixed: wraps the future in .instrument(span) instead of holding enter() across the await, matching the pattern already used in core/context/mod.rs.
df3446e
| // cleans up the map entry on every exit path, including cancellation. Never read | ||
| // (its only job is running `Drop` at end of scope), hence the `_` prefix. | ||
| let _guard = WaiterGuard::new(nonce, self.request_id_map.clone()); |
There was a problem hiding this comment.
Added a cancellation test: spawns get_max_level, aborts it mid-flight before any reply is delivered, and asserts the waiter-map entry is cleaned up.
8cda86c
|
Claude (@claude) review |
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
Claude finished TheP2P (@thep2p)'s task —— View job Code ReviewReviewed the request-correlation generalization. Overall this is well-crafted work: the Correctness / design
Minor / nits
Tests
Unrelated diffs: Nothing blocking. LGTM once the minor prose nit (#3) is optionally cleaned up. Note: I was unable to run |
|
Fixed the dangling sentence in the The other item ( |
Generalizes
BaseNoderequest correlation so one nonce-keyed map serves every messagetype, and adds the phase-0 join bootstrap query on top of it.
src/node/waiter.rs(new).Waitercarries one variant per message type, ablocking
SyncSenderforsearch_by_idand aoneshot::Senderforget_max_level,because the two callers differ in concurrency shape.
WaiterGuardties map cleanup torequest scope via
Drop, the only cleanup an async caller cannot skip by dropping itsfuture mid-await.
get_max_level. Asks an introducer for its highest populated lookup-table levelunder a caller-supplied timeout.
RetMaxLevelOpresolves by nonce, and a wrong-variant or expired entry isa no-op.
Tests cover the single-request resolve, the timeout plus map cleanup, and a blocking and
an async waiter live in the map at once.
Closes #88